在開始學習Shader之前,我們應該先了解一張2D圖片是怎麼從Unity的3D場景被繪製生成,或者說是渲染出來的,也就是Render Pipeline的工作流程。
Render Pipeline是從3D場景,渲染出一張2D的生產線,藉由CPU和GPU的通力合作,將虛擬相機、物件、Texture、Shader等資料,轉換成顯示在螢幕上的圖片。
一個完整的渲染流程,可以分為三個階段:Application Stage(應用階段)、Geometry Stage(幾何階段)、Rasterizer Stage(光柵化階段)。每個階段底下又可以再細分成子流水線。
Application Stage:顧名思義,是由程式主導,也就是由開發者全權掌握,包括3D場景的建立、剔除不可見的物體(culling),以及每個模型的材質、光源、Shader的設定。這個階段會輸出Rendering Primitives(渲染圖元),也就是構成場景的點、線、三角面。
Geomertry Stage:這個階段會將從Application Stage輸入的3D點、線、面資訊投射到螢幕上,產成2D頂點座標,同時也會決定每個頂點對應的深度值、顏色的相關資訊。
Rasterizer Stage:這個階段根據將2D頂點座標的資訊,繪製螢幕上的一個個的像素,產生最終的圖像。
Application Stage由於是由應用主導,所以是在CPU處理。Geometry Stage和Rasterizer Stage則是真正在處理影像的階段,在GPU處理。
CPU和GPU兩者之間的溝通,是透過Draw Call。Draw Call是由CPU發送需求給GPU,調用圖像繪製的介面(在Unity就是OpenGL的Interface),CPU會將需要渲染的對象/圖元,交給GPU進行後兩階段的處理。
Draw Call和渲染的性能優化息息相關。每提交一筆Draw Call,CPU就需要向GPU發送很多內容,包括渲染的資訊等等,因此當Draw Call一變多,CPU的負擔就變重,進而拖累效能。
Unity減少Draw Call的方法,最主要是依靠Batch(批次處理),將多個Draw Call合併成單一一筆大的Draw Call進行處理。既然要合併,他們的內容就要有一致性才行,比如說使用共用材質或將多個2D素材合併成圖集。在美術階段就要小心留意資源的使用,才不會在後續的開發時,還需要回過頭來處理素材的問題。
另外,對於UI物件而言,即使在相機外,也是會發送Draw Call的。唯有將需要渲染的對象/Image Compoent關閉,才會真正減少Draw Call的產生。
如果想了解更多如何優化Draw Call的細節,個人非常推薦去讀讀看這篇文章:Unity Draw Call Batching: The Ultimate Guide,作者從自身經驗出發,詳細的描述如何減少Draw Call並優化遊戲性能。
本篇描述的Render Pipeline不只適用於Unity,也同樣適用於其他平台。下篇文章,將會針對Geometry Stage和Rasterizer Stage進行更詳細的描述。
*本系列文章是在《Unity Shader入門精要》一書的基礎上,加上個人實際操作下經驗的總結產出的。如果有任何問題或說錯的地方,歡迎提出。